package toolkit.dart.porting.processor;

import java.util.HashMap;
import java.util.Map;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;

public class PortingProcessor {
	
	private static final String DART_BOOL_TYPE = "bool";

	private static final String STRING_TYPE = "String";

	private static final String DOUBLE_TYPE = "double";

	private static final String BOOLEAN_TYPE = "boolean";

	private static final String[] PRIMITIVE_TYPES = new String[] {"byte", "short", "int", "long", "float", DOUBLE_TYPE, "char", BOOLEAN_TYPE};
	
	private static final String NEW_KEYWORD = "new";
	private static final String EMPTY_BRACES = "[]";
	private static final String WHITESPACE_BRACES_PATTERN = "\\[\\s+\\]";
	private static final String PACKAGE_PATTERN = "package [a-zA-Z\\.]+;";
	private static final String IMPORT_PATTERN = "import [a-zA-Z\\.]+;";
	private static String[] removeKeywords = new String[] {"public", "protected", "private"};
	
	private static Map<String, String> idReplacement = new HashMap<String,String>();
	
	static {
		idReplacement.put("boolean","bool");
		idReplacement.put("ArrayList","List");
		idReplacement.put("HashMap","Map");
	}
	
	public static String process(String source) throws BadLocationException {
		String result = source;
		for (String keyword : removeKeywords) {
			result = result.replaceAll(keyword + " ","");
		}
		result = result.replaceAll(PACKAGE_PATTERN,"");
		result = result.replaceAll(IMPORT_PATTERN,"");
		result = result.replaceAll(WHITESPACE_BRACES_PATTERN,EMPTY_BRACES);
		int index = result.indexOf(EMPTY_BRACES);
		Document document = new Document(result);
		doIdReplacement(document);
		result = document.get();
		while (index > -1) {
			int bracesEnd = index + EMPTY_BRACES.length();
			while (bracesEnd < result.length() && Character.isWhitespace(result.charAt(bracesEnd))) {
				bracesEnd++;
			}
			int idEnd = index - 1;
			idEnd = skipWhitespaceBefore(result, idEnd);
			int idStart = findIdStart(result, idEnd);
			String id = result.substring(idStart, idEnd + 1);
			if (bracesEnd < result.length() && result.charAt(bracesEnd) == '{') {
				bracesEnd++;
				int initializerEnd = findBlockEnd(result,bracesEnd);
				if (initializerEnd < bracesEnd) {
					return null;
				}
				int prevIdStart = skipWhitespaceBefore(result, idStart - 1);
				String token = readPrevToken(prevIdStart, result);
				if (NEW_KEYWORD.equals(token)) {
					idStart = prevIdStart - NEW_KEYWORD.length() + 1;
				}
				document.replace(initializerEnd,1,"]");
				document.replace(idStart,bracesEnd-idStart,"[");
				result = document.get();
			} else {
				document.replace(idStart,bracesEnd-idStart,"List ");
				result = document.get();
			}
			index = result.indexOf(EMPTY_BRACES);
		}
		int braceStart = result.indexOf('[');
		while (braceStart > -1) {
			int idEnd = braceStart - 1;
			int braceEnd = findBraceEnd(result, braceStart + 1);
			if (braceEnd < braceStart) {
				return null;
			}
			String group = result.substring(braceStart + 1, braceEnd);
			idEnd = skipWhitespaceBefore(result, idEnd);
			int idStart = findIdStart(result, idEnd);
			if (idStart < idEnd) { //Otherwise, there is no id before the brace & this brace is already a result of porting
				String id = result.substring(idStart, idEnd + 1);
				int prevTokenEnd = skipWhitespaceBefore(result, idStart - 1);
				String token = readPrevToken(prevTokenEnd, result);
				if (NEW_KEYWORD.equals(token)) { //new array initialization  - replace with "new List(n)"
					if (isPrimitive(id)) {
						String newType = convertPrimitiveType(id);
						document.replace(idStart,braceEnd - idStart + 1, " List<" + newType + ">.filled(" + group + ", " + getDefaultValue(newType) + ")");
					} else {
						document.replace(idStart,braceEnd - idStart + 1, " List(" + group + ")");
					}
					result = document.get();
				} else { //replace a[n] in expression with a.get(n)
					document.replace(braceStart,braceEnd - braceStart + 1, ".elementAt(" + group + ")");
					result = document.get();				
				}
			}
			braceStart = result.indexOf('[', braceStart + 1);
		}
		return result.trim();
	}

	private static void doIdReplacement(Document document) throws BadLocationException {
		String text = document.get();
		for (String id : idReplacement.keySet()) {
			int idx = text.indexOf(id);
			while (idx >= 0) {
				int nextCharIdx = idx + id.length();
				if ((idx == 0 || !Character.isJavaIdentifierStart(text.charAt(idx - 1))) &&
					(nextCharIdx == text.length() || !Character.isJavaIdentifierPart(text.charAt(nextCharIdx)))) {
					String replacement = idReplacement.get(id);
					document.replace(idx, id.length(), replacement);
					text = document.get();
					idx = text.indexOf(id, idx + replacement.length());
				} else {
					idx = text.indexOf(id, idx + id.length());
				}
			}
			
		}
	}

	private static String convertPrimitiveType(String id) {
		if ("short".equals(id) || "long".equals(id) || "byte".equals(id))
			return "int";
		if ("float".equals(id)) {
			return DOUBLE_TYPE;
		}
		if ("char".equals(id)) {
			return STRING_TYPE;
		}
		if (BOOLEAN_TYPE.equals(id)) {
			return DART_BOOL_TYPE;
		}
		return id;
	}

	private static String getDefaultValue(String id) {
		if (DART_BOOL_TYPE.equals(id)) {
			return "false";
		}
		if (DOUBLE_TYPE.equals(id)) {
			return "0.0";
		}
		if (STRING_TYPE.equals(id)) {
			return "\"\"";
		}
		return "0";
	}

	private static boolean isPrimitive(String id) {
		for (String type : PRIMITIVE_TYPES) {
			if (type.equals(id)) {
				return true;
			}
		}
		return false;
	}

	protected static int findIdStart(String result, int idEnd) {
		int idStart = idEnd;
		while (idStart > 0 && Character.isJavaIdentifierPart(result.charAt(idStart))) {
			idStart--;
		}
		if (idStart < idEnd && !Character.isJavaIdentifierPart(result.charAt(idStart))) {
			idStart++;
		}
		return idStart;
	}

	private static int findBlockEnd(String string, int from) {
		return findEnd('{','}',string,from);
	}
	
	private static int findBraceEnd(String string, int from) {
		return findEnd('[',']',string,from);
	}
	
	private static int findEnd(char startChar, char endChar, String string, int from) {
		int level = 1;
		int idx = from;
		while (idx < string.length()) {
			char curChar = string.charAt(idx);
			if (curChar == startChar) {
				level++;
			} else if (curChar == endChar) {
				level--;
				if (level == 0) {
					return idx;
				}
			}
			idx++;
		}
		return -1;
	}

	private static boolean isId(String token) {
		if (token.length() == 0) {
			return false;
		}
		if (!Character.isJavaIdentifierStart(token.charAt(0))) {
			return false;
		}
		for (int i = 1; i < token.length(); i++) {
			if (!Character.isJavaIdentifierPart(token.charAt(i))) {
				return false;
			}
		}
		return true;
	}

	private static int skipWhitespaceBefore(String result, int idEnd) {
		while (idEnd > 0 && Character.isWhitespace(result.charAt(idEnd))) {
			idEnd--;
		}
		return idEnd;
	}
	
	private static String readPrevToken(int idx, String string) {
		StringBuilder builder = new StringBuilder();
		while (idx > 0 && !Character.isWhitespace(string.charAt(idx))) {
			builder.append(string.charAt(idx));
			idx--;
		}
		builder.reverse();
		return builder.toString();
	}
}
